<template>
  <div class="cloud-ptz-control">
    <div class="header">
        <h1>智慧社区视频云监控中心</h1>
        <p>实时监控多路高清摄像头视频信号，保障社区安全</p>
    </div>

    <div class="controls">
        <button class="layout-btn active" data-layout="1" @click="changeLayout(1)">单画面模式</button>
    </div>

    <div class="grid layout-1" id="video-grid"></div>

    <div class="fullscreen-container" id="fullscreen-container">
        <button class="fullscreen-close" @click="closeFullscreen">×</button>
        <div class="fullscreen-content">
            <video id="fullscreen-video" class="fullscreen-video" controls autoplay muted></video>
        </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'CloudPtzControl',
  data() {
    return {
      streams: [
        { id: '1', name: '大门口', url: '/api/flv/monitor/1' },
        { id: '2', name: '小区门禁', url: '/api/flv/monitor/2' },
        { id: '3', name: '1号楼前', url: '/api/flv/monitor/3' },
        { id: '4', name: '2号楼前', url: '/api/flv/monitor/4' },
        { id: '5', name: '停车场', url: '/api/flv/monitor/5' }
      ],
      players: {},
      currentLayout: 1,
      activeFullscreenPlayer: null,
      reconnectAttempts: {},
      maxReconnectAttempts: 3,
      reconnectInterval: 5000, // 5秒重连间隔
      selectedId: '',
      idList: [1, 2, 3, 4, 5]
    };
  },
  mounted() {
    // 确保flv.js已加载
    this.loadFlvJs().then(() => {
      this.initPage();
    }).catch(error => {
      console.error('无法加载flv.js:', error);
    });
  },
  beforeDestroy() {
    // 清理资源
    Object.keys(this.players).forEach(id => {
      if (this.players[id] && this.players[id].player) {
        this.players[id].player.destroy();
      }
    });
    
    if (this.activeFullscreenPlayer) {
      this.activeFullscreenPlayer.destroy();
    }
  },
  methods: {
    // 加载flv.js
    loadFlvJs() {
      return new Promise((resolve, reject) => {
        if (window.flvjs) {
          resolve(window.flvjs);
          return;
        }
        
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js';
        script.async = true;
        script.onload = () => resolve(window.flvjs);
        script.onerror = () => reject(new Error('flv.js加载失败'));
        document.head.appendChild(script);
      });
    },
    
    // 初始化页面
    initPage() {
      this.renderGrid(this.currentLayout);
    },
    
    // 根据布局渲染网格
    renderGrid(count) {
      const grid = document.getElementById('video-grid');
      grid.innerHTML = '';
      
      for (let i = 0; i < count; i++) {
        const windowId = `window-${i}`;
        const window = this.createVideoWindow(windowId, i);
        grid.appendChild(window);
      }
      
      // 初始化播放器
      for (let i = 0; i < count; i++) {
        const windowId = `window-${i}`;
        const streamId = i < this.streams.length ? this.streams[i].id : this.streams[0].id;
        this.setupStream(windowId, streamId);
      }
    },
    
    // 创建视频窗口
    createVideoWindow(id, index) {
      const div = document.createElement('div');
      div.className = 'window';
      div.id = id;
      
      // 选择默认流
      const defaultStream = index < this.streams.length ? this.streams[index] : this.streams[0];
      
      div.innerHTML = `
        <div class="window-header">
            <h3 class="window-title">监控画面 ${index + 1}</h3>
            <div class="window-controls">
                <button class="window-btn play-btn" title="播放" onclick="document.querySelector('[data-window-id=${id}]').play()">▶</button>
                <button class="window-btn stop-btn" title="停止" onclick="document.querySelector('[data-window-id=${id}]').stop()">■</button>
                <button class="window-btn expand-btn" title="全屏" onclick="document.querySelector('[data-window-id=${id}]').expand()">⤢</button>
            </div>
        </div>
        <video id="video-${id}" muted></video>
        <select class="window-dropdown" id="stream-select-${id}" onchange="document.querySelector('[data-window-id=${id}]').changeStreamSelection(this.value)">
            ${this.streams.map(stream => 
                `<option value="${stream.id}" ${stream.id === defaultStream.id ? 'selected' : ''}>${stream.name}</option>`
            ).join('')}
        </select>
        <div class="status-bar">
            <div><span class="status-indicator" id="status-indicator-${id}"></span> <span id="status-text-${id}">信号状态</span></div>
            <div id="bitrate-${id}"></div>
        </div>
      `;
      
      // 添加自定义属性用于事件绑定
      div.setAttribute('data-window-id', id);
      
      // 绑定方法
      div.play = () => this.playStream(id);
      div.stop = () => this.stopStream(id);
      div.expand = () => this.expandStream(id);
      div.changeStreamSelection = (value) => this.changeStream(id, value);
      
      return div;
    },
    
    // 改变布局
    changeLayout(count) {
      // 先停止所有流
      Object.keys(this.players).forEach(id => {
        if (this.players[id]) {
          this.stopStream(id);
        }
      });
      
      // 清空播放器对象
      this.players = {};
      
      // 重新渲染网格
      this.currentLayout = count;
      this.renderGrid(count);
    },
    
    // 设置流
    setupStream(windowId, streamId) {
      const flvjs = window.flvjs;
      if (!flvjs) return;
      
      const stream = this.streams.find(s => s.id === streamId);
      if (!stream) return;
      
      const videoElement = document.getElementById(`video-${windowId}`);
      const statusIndicator = document.getElementById(`status-indicator-${windowId}`);
      const statusText = document.getElementById(`status-text-${windowId}`);
      
      this.updateStatus(windowId, 'connecting');
      
      if (flvjs.isSupported()) {
        // 初始化重连次数
        if (!this.reconnectAttempts[windowId]) {
          this.reconnectAttempts[windowId] = 0;
        }

        const player = flvjs.createPlayer({
          type: 'flv',
          url: stream.url,
          cors: true,
          isLive: true,
          hasAudio: true,
          hasVideo: true,
          enableStashBuffer: false
        });
        
        player.attachMediaElement(videoElement);
        player.load();
        
        player.on(flvjs.Events.ERROR, (errorType, errorDetail) => {
          this.updateStatus(windowId, 'error');
          console.error(`流播放错误: ${errorType}, 详情: ${errorDetail}`);
          
          // 尝试重连，但限制重连次数
          if (this.reconnectAttempts[windowId] < this.maxReconnectAttempts) {
            this.reconnectAttempts[windowId]++;
            console.log(`尝试重新连接 ${stream.name}，第 ${this.reconnectAttempts[windowId]} 次尝试`);
            
            setTimeout(() => {
              if (this.players[windowId]) {
                this.stopStream(windowId);
                this.setupStream(windowId, streamId);
              }
            }, this.reconnectInterval);
          } else {
            console.error(`${stream.name} 达到最大重连次数，停止重连`);
          }
        });
        
        player.on(flvjs.Events.MEDIA_INFO, (mediaInfo) => {
          console.log(`${stream.name} 媒体信息:`, mediaInfo);
        });
        
        player.on(flvjs.Events.RECOVERED_EARLY_EOF, () => {
          console.log(`${stream.name} 从提前结束中恢复`);
        });

        player.on(flvjs.Events.STATISTICS_INFO, (stats) => {
          if (stats && stats.speed) {
            const bitrateElement = document.getElementById(`bitrate-${windowId}`);
            if (bitrateElement) {
              const kbps = Math.round(stats.speed * 8 / 1024);
              bitrateElement.textContent = `${kbps} kbps`;
            }
          }
        });
        
        this.players[windowId] = {
          player,
          streamId,
          playing: true
        };
        
        player.play().then(() => {
          this.updateStatus(windowId, 'connected');
          // 重置重连计数
          this.reconnectAttempts[windowId] = 0;
        }).catch(() => {
          this.updateStatus(windowId, 'error');
        });
      } else {
        this.updateStatus(windowId, 'error');
        if (statusText) statusText.textContent = '浏览器不支持FLV.js';
      }
    },
    
    // 播放流
    playStream(windowId) {
      if (this.players[windowId] && !this.players[windowId].playing) {
        const streamId = document.getElementById(`stream-select-${windowId}`).value;
        this.setupStream(windowId, streamId);
      } else if (!this.players[windowId]) {
        const streamId = document.getElementById(`stream-select-${windowId}`).value;
        this.setupStream(windowId, streamId);
      }
    },
    
    // 停止流
    stopStream(windowId) {
      if (this.players[windowId]) {
        const player = this.players[windowId].player;
        if (player) {
          player.pause();
          player.unload();
          player.detachMediaElement();
          player.destroy();
        }
        
        this.players[windowId].playing = false;
        // 重置重连计数
        this.reconnectAttempts[windowId] = 0;
        this.updateStatus(windowId, 'stopped');
      }
    },
    
    // 切换流
    changeStream(windowId, streamId) {
      this.stopStream(windowId);
      this.setupStream(windowId, streamId);
    },
    
    // 更新状态
    updateStatus(windowId, status) {
      const statusIndicator = document.getElementById(`status-indicator-${windowId}`);
      const statusText = document.getElementById(`status-text-${windowId}`);
      
      if (!statusIndicator || !statusText) return;
      
      statusIndicator.className = 'status-indicator';
      
      switch(status) {
        case 'connecting':
          statusText.textContent = '正在连接...';
          break;
        case 'connected':
          statusIndicator.classList.add('connected');
          statusText.textContent = '信号正常';
          if (this.players[windowId]) this.players[windowId].playing = true;
          break;
        case 'error':
          statusIndicator.classList.add('error');
          statusText.textContent = '信号异常';
          if (this.players[windowId]) this.players[windowId].playing = false;
          break;
        case 'stopped':
          statusText.textContent = '监控已停止';
          if (this.players[windowId]) this.players[windowId].playing = false;
          break;
      }
    },
    
    // 全屏查看
    expandStream(windowId) {
      const flvjs = window.flvjs;
      if (!flvjs) return;
      
      if (!this.players[windowId]) return;
      
      const streamId = this.players[windowId].streamId;
      const stream = this.streams.find(s => s.id === streamId);
      if (!stream) return;
      
      const fullscreenContainer = document.getElementById('fullscreen-container');
      const fullscreenVideo = document.getElementById('fullscreen-video');
      
      fullscreenContainer.style.display = 'flex';
      
      if (this.activeFullscreenPlayer) {
        this.activeFullscreenPlayer.pause();
        this.activeFullscreenPlayer.unload();
        this.activeFullscreenPlayer.detachMediaElement();
        this.activeFullscreenPlayer.destroy();
        this.activeFullscreenPlayer = null;
      }
      
      if (flvjs.isSupported()) {
        this.activeFullscreenPlayer = flvjs.createPlayer({
          type: 'flv',
          url: stream.url
        });
        
        this.activeFullscreenPlayer.attachMediaElement(fullscreenVideo);
        this.activeFullscreenPlayer.load();
        this.activeFullscreenPlayer.play();
      }
    },
    
    // 关闭全屏
    closeFullscreen() {
      const fullscreenContainer = document.getElementById('fullscreen-container');
      fullscreenContainer.style.display = 'none';
      
      if (this.activeFullscreenPlayer) {
        this.activeFullscreenPlayer.pause();
        this.activeFullscreenPlayer.unload();
        this.activeFullscreenPlayer.detachMediaElement();
        this.activeFullscreenPlayer.destroy();
        this.activeFullscreenPlayer = null;
      }
    },
  }
};
</script>

<style scoped>
.cloud-ptz-control {
    font-family: 'Microsoft YaHei', Arial, sans-serif;
    padding: 20px;
    background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
    min-height: 100%;
    border-radius: 8px;
    overflow: hidden;
}
.header {
    color: white;
    text-align: center;
    margin-bottom: 30px;
}
.header h1 {
    margin: 0;
    font-size: 2.5em;
    font-weight: 300;
}
.header p {
    margin: 10px 0 0 0;
    color: rgba(255, 255, 255, 0.9);
    font-size: 1.1em;
}
.controls {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
    gap: 15px;
}
.layout-btn {
    background: linear-gradient(45deg, #2196F3, #21CBF3);
    color: white;
    border: none;
    padding: 8px 20px;
    border-radius: 25px;
    cursor: pointer;
    font-size: 15px;
    transition: all 0.3s ease;
    box-shadow: 0 4px 10px rgba(33, 150, 243, 0.3);
}
.layout-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 15px rgba(33, 150, 243, 0.4);
}
.layout-btn.active {
    background: linear-gradient(45deg, #3f51b5, #5c6bc0);
}
.grid.layout-1 {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 60vh;
    padding: 0;
}
.window {
    background: #fff;
    border-radius: 16px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.25);
    padding: 20px;
    overflow: hidden;
    position: relative;
    border: 2px solid #2c3e50;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 800px;
    max-width: 800px;
    height: 600px;
    max-height: 600px;
    margin: 0 auto;
}
.window:hover {
    transform: translateY(-5px);
    box-shadow: 0 15px 35px rgba(0,0,0,0.3);
}
.window-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid #e0e0e0;
}
.window-title {
    font-size: 16px;
    font-weight: 600;
    color: #1e4b7a;
    margin: 0;
    text-shadow: 0 1px 1px rgba(0,0,0,0.1);
}
.window-controls {
    display: flex;
    gap: 5px;
}
.window-btn {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    transition: all 0.2s;
}
.play-btn {
    background: #4caf50;
    color: white;
}
.play-btn:hover {
    background: #43a047;
}
.stop-btn {
    background: #f44336;
    color: white;
}
.stop-btn:hover {
    background: #e53935;
}
.window video {
    width: 100%;
    height: 100%;
    max-width: 760px;
    max-height: 560px;
    min-width: 400px;
    min-height: 300px;
    background: #000;
    border-radius: 12px;
    border: 2px solid #666;
    box-shadow: 0 0 20px rgba(0,0,0,0.5);
    object-fit: cover;
    margin-bottom: 10px;
}
.status-bar {
    font-size: 12px;
    margin-top: 8px;
    color: #757575;
    display: flex;
    justify-content: space-between;
}
.status-indicator {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    margin-right: 5px;
    background: #bdbdbd;
}
.status-indicator.connected {
    background: #4caf50;
}
.status-indicator.error {
    background: #f44336;
}
.window-dropdown {
    margin-top: 10px;
    width: 100%;
    padding: 8px;
    border-radius: 4px;
    border: 1px solid #ddd;
    font-size: 14px;
}
.fullscreen-container {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.9);
    z-index: 1000;
    display: none;
    justify-content: center;
    align-items: center;
    padding: 20px;
}
.fullscreen-content {
    width: 90%;
    max-width: 1200px;
}
.fullscreen-video {
    width: 100%;
    height: 80vh;
    background: #000;
}
.fullscreen-close {
    position: absolute;
    top: 20px;
    right: 20px;
    color: white;
    background: rgba(0,0,0,0.5);
    border: none;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    font-size: 20px;
    cursor: pointer;
}
.expand-btn {
    background: #ff9800;
    color: white;
}
.expand-btn:hover {
    background: #f57c00;
}
.channel-selector-bar {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 30px;
}
.channel-selector {
    width: 220px;
    font-size: 18px;
}
@media (max-width: 768px) {
    .grid.layout-2, .grid.layout-4, .grid.layout-9 {
        grid-template-columns: 1fr;
    }
}
</style> 